package com.mdb.open.epay.validator;

import com.alibaba.fastjson.JSON;
import com.mdb.bc.api.data.ResultEx;
import com.mdb.bc.cache.CacheKeyGenerator;
import com.mdb.bc.cache.MdbCache;
import com.mdb.bc.utils.ObjectUtil;
import com.mdb.open.epay.cache.CacheConfig;
import com.mdb.open.epay.cache.PayOtpBean;
import com.mdb.open.epay.cache.SignOtpBean;
import com.mdb.open.epay.config.EPayMsgConstant;
import com.mdb.open.epay.config.EPayOpenProperties;
import com.mdb.open.epay.domain.bo.PayBo;
import com.mdb.open.epay.domain.bo.SendPayOtpBo;
import com.mdb.open.epay.domain.bo.SendSignOtpBo;
import com.mdb.open.epay.domain.bo.SignBo;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.StringJoiner;
import java.util.concurrent.TimeUnit;

/**
 * otp相關校驗器
 *
 * @author liuyi
 * @date 2019/11/14
 **/
@Log4j2
@Component
public class OtpValidator {

    @Autowired
    private MdbCache mdbCache;

    @Autowired
    private EPayOpenProperties ePayOpenProperties;

    /******簽約OTP******/

    public void setSignOtpCache(SendSignOtpBo otpBo, String coreClientNo, String engName, String chnName, String otpCode) {
        //生成验证码，存入到缓存
        SignOtpBean signOtpBean = new SignOtpBean();
        signOtpBean.setBankCardNo(otpBo.getBankCardNo());
        signOtpBean.setPhoneArea(otpBo.getPhoneArea());
        signOtpBean.setPhoneNo(otpBo.getPhoneNo());
        signOtpBean.setCertType(otpBo.getCertType());
        signOtpBean.setCertNo(otpBo.getCertNo());
        signOtpBean.setCoreClientNo(coreClientNo);
        signOtpBean.setOtpCode(otpCode);
        //新增加的字段
        signOtpBean.setChnName(chnName);
        signOtpBean.setEngName(engName);

        String cacheKey = signOtpCacheKeyGen(signOtpBean);
        mdbCache.setEx(cacheKey, JSON.toJSONString(signOtpBean), ePayOpenProperties.getSignOptCodeExpireMillis(), TimeUnit.MILLISECONDS);
        //清空Otp验证码验证错误次数
        cleanSignOtpCheckTimes(signOtpBean);
    }

    /**
     * 清空otp验证错误次数
     */
    private void cleanSignOtpCheckTimes(SignOtpBean signOtpBean) {
        String timesCacheKey = signTimesKeyGen(signOtpBean);
        mdbCache.delete(timesCacheKey);
    }

    /**
     * 验证码验证
     * 检查验证错误次数，达到上限则直接要求重新发送验证码
     * 验证验证码是否正确，错误则错误次数加1
     */
    public ResultEx<SignOtpBean> checkSignOtp(SignBo bo) {
        SignOtpBean signOtpBean = new SignOtpBean();
        signOtpBean.setBankCardNo(bo.getBankCardNo());
        signOtpBean.setPhoneArea(bo.getPhoneArea());
        signOtpBean.setPhoneNo(bo.getPhoneNo());
        signOtpBean.setCertType(bo.getCertType());
        signOtpBean.setCertNo(bo.getCertNo());

        String timesCacheKey = signTimesKeyGen(signOtpBean);
        String otpCacheKey = signOtpCacheKeyGen(signOtpBean);

        Long value = mdbCache.increment(timesCacheKey, 1, ePayOpenProperties.getSignOptCodeExpireMillis(), TimeUnit.MILLISECONDS);
        if (value >= ePayOpenProperties.getOptMaxErrorTimes()) {
            return ResultEx.fail(EPayMsgConstant.OTP_ERROR_TOO_MANY_TIMES);//TODO 验证错误次数过多，请重新发送OTP
        }
        //验证验证码
        String valueStr = mdbCache.get(otpCacheKey);

        SignOtpBean cacheSignOtpBean = JSON.parseObject(valueStr, SignOtpBean.class);

        if (ObjectUtil.isEmpty(cacheSignOtpBean)) {
            return ResultEx.fail(EPayMsgConstant.OTP_ERROR);//TODO 验证码错误
        }

        if(ObjectUtil.isNotEmpty(bo.getBankAccountNameC())) {
            if(!bo.getBankAccountNameC().equals(cacheSignOtpBean.getChnName())) {
                return ResultEx.fail("與銀行預留的中文姓名不一致");//TODO
            }
        }

        if(!bo.getBankAccountNameE().equals(cacheSignOtpBean.getEngName())) {
            return ResultEx.fail("與銀行預留的英文姓名不一致");//TODO
        }

        if (cacheSignOtpBean.getOtpCode().equals(bo.getMsgCode()) || !ePayOpenProperties.isOtpCheck()) {
            //验证成功，清空验证码缓存
            mdbCache.delete(otpCacheKey);
            return ResultEx.ok(cacheSignOtpBean);
        }

        return ResultEx.fail(EPayMsgConstant.OTP_ERROR);//TODO 验证码错误

    }

    /**
     * 生成otp验证码缓存Key
     */
    private String signOtpCacheKeyGen(SignOtpBean signOtpBean) {
        return new CacheKeyGenerator(CacheConfig.OTP_KEY_PREFIX)
                .generate(signOtpBean.getCertType(), signOtpBean.getPhoneArea(),
                        new StringJoiner("_")
                                .add(signOtpBean.getCertNo())
                                .add(signOtpBean.getPhoneNo())
                                .add(signOtpBean.getBankCardNo()).toString());
    }

    /**
     * 生成otp验证码验证次数缓存Key
     */
    private String signTimesKeyGen(SignOtpBean signOtpBean) {
        return new CacheKeyGenerator(CacheConfig.OTP_TIMES_KEY_PREFIX)
                .generate(signOtpBean.getCertType(), signOtpBean.getPhoneArea(),
                        new StringJoiner("_")
                                .add(signOtpBean.getCertNo())
                                .add(signOtpBean.getPhoneNo())
                                .add(signOtpBean.getBankCardNo()).toString());
    }


    /******支付OTP******/

    public void setTradeOtpCache(SendPayOtpBo otpBo, String otpCode) {
        //生成验证码，存入到缓存
        PayOtpBean tradeOtpBean = new PayOtpBean();
        tradeOtpBean.setOrderNo(otpBo.getOrderNo());
        tradeOtpBean.setSignNo(otpBo.getBankSignNo());
        tradeOtpBean.setOtpCode(otpCode);

        String cacheKey = tradeOtpCacheKeyGen(tradeOtpBean);
        log.info("pay otp cacheKey:{}", cacheKey);
        mdbCache.setEx(cacheKey, JSON.toJSONString(tradeOtpBean), ePayOpenProperties.getTradeOptCodeExpireMillis(), TimeUnit.MILLISECONDS);
        //清空Otp验证码验证错误次数
        cleanTradeOtpCheckTimes(tradeOtpBean);
    }

    /**
     * 清空otp验证错误次数
     */
    private void cleanTradeOtpCheckTimes(PayOtpBean signOtpBean) {
        String timesCacheKey = tradeTimesKeyGen(signOtpBean);
        mdbCache.delete(timesCacheKey);
    }

    /**
     * 验证码验证
     * 检查验证错误次数，达到上限则直接要求重新发送验证码
     * 验证验证码是否正确，错误则错误次数加1
     */
    public ResultEx<PayOtpBean> checkTradeOtp(PayBo bo) {
        PayOtpBean payOtpBean = new PayOtpBean();
        payOtpBean.setOrderNo(bo.getOrderNo());
        payOtpBean.setSignNo(bo.getBankSignNo());

        String timesCacheKey = tradeTimesKeyGen(payOtpBean);
        String otpCacheKey = tradeOtpCacheKeyGen(payOtpBean);
        log.info("valid pay otp timesCacheKey:{}, otpCacheKey:{}", timesCacheKey, otpCacheKey);
        Long value = mdbCache.increment(timesCacheKey, 1, ePayOpenProperties.getTradeOptCodeExpireMillis(), TimeUnit.MILLISECONDS);
        if (value >= ePayOpenProperties.getOptMaxErrorTimes()) {
            return ResultEx.fail(EPayMsgConstant.OTP_ERROR_TOO_MANY_TIMES);//TODO 验证错误次数过多，请重新发送OTP
        }
        //验证验证码
        String valueStr = mdbCache.get(otpCacheKey);

        PayOtpBean cachePayOtpBean = JSON.parseObject(valueStr, PayOtpBean.class);
        log.info("");
        if (ObjectUtil.isEmpty(cachePayOtpBean)) {
            return ResultEx.fail(EPayMsgConstant.OTP_ERROR);
        }
        if (bo.getMsgCode().equals(cachePayOtpBean.getOtpCode()) || !ePayOpenProperties.isOtpCheck()) {
            //验证成功，清空验证码缓存
            mdbCache.delete(otpCacheKey);
            return ResultEx.ok(cachePayOtpBean);
        }

        return ResultEx.fail(EPayMsgConstant.OTP_ERROR);//TODO 验证码错误

    }

    /**
     * 生成交易otp验证码缓存Key
     */
    private String tradeOtpCacheKeyGen(PayOtpBean otpBean) {
        return new CacheKeyGenerator(CacheConfig.OTP_KEY_PREFIX)
                .generate(otpBean.getOrderNo(), otpBean.getSignNo());
    }

    /**
     * 生成交易otp验证码验证次数缓存Key
     */
    private String tradeTimesKeyGen(PayOtpBean otpBean) {
        return new CacheKeyGenerator(CacheConfig.OTP_TIMES_KEY_PREFIX)
                .generate(otpBean.getOrderNo(), otpBean.getSignNo());
    }

}
